//Path: ISPD_Contest/sizer-v1.0

#include "optimization.h"
//#include "coneSampling.h"
#include "utility.h"
#include <algorithm>
#include "extendedverilogparser.h"
#include "timer_interface.h"
#include "time.h"
#include "thread.h"
#include <ctime>


#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>

#include "validate.cpp"
#include "parser_helper.h"
#include "extendedlibparser.h"
#include "extendedtimingparser.h"
#include "extendedspefparser.h"
#include "extendedsdcparser.h"
#include "extendedverilogparser.h"

using namespace std ;
typedef boost::mt19937 gen;

//global variables, Min
CircuitGraph* graphInPtr;
ExtendedSdcParser* sdcPtr;
ExtendedLibParser* lpPtr;
ExtendedTimingParser* tpPtr;
//vector<string> optList;

bool lessLeakage(const string &s1, const string &s2)
{
        LibParserCellInfo *LPCI1, *LPCI2;
        LPCI1 = &(lpPtr->mapStandardLibraryCells[s1]);
        LPCI2 = &(lpPtr->mapStandardLibraryCells[s2]);
        
        return (LPCI1->leakagePower < LPCI2->leakagePower);
}

void minLeakage (CircuitGraph& graphIn)
{
        int i,j;
        int numberOfCells;
        LibParserCellInfo *LPCI;
        
        double temp_leakage;
        string selected_cell_name;
        cout << "Choosing minimum leakage cells...." << endl;
        
        int vn = boost::num_vertices(graphIn) ;
        cout << "Number of gates: " << vn << endl ;
        
        for( i = 0; i < vn; i++)
        {
                
                temp_leakage = 10000000.0;//larger enough?
                numberOfCells = graphIn[i].listOfCells.size() ;

                for(j = 0; j < numberOfCells; j++)
                {
                        LPCI = &(lpPtr->mapStandardLibraryCells[graphIn[i].listOfCells[j]]);
                        if(LPCI->leakagePower < temp_leakage)
                        {
                                temp_leakage = LPCI->leakagePower;
                                selected_cell_name = graphIn[i].listOfCells[j];
                        }
                }
                
                if(numberOfCells > 1)//get rid of the DFFs
                        graphIn[ i ].cellName = selected_cell_name;
                
                LPCI = &(lpPtr->mapStandardLibraryCells[ graphIn[ i ].cellName ]);
                
                //sort the cells of same footprint in less leakage order
                std::sort(graphIn[i].listOfCells.begin(),graphIn[i].listOfCells.end(),lessLeakage);                
        }
        
}

bool moreViolation(const int i1, const int i2)
{
        return ((*graphInPtr)[i1].Slew_Violation + (*graphInPtr)[i1].Slack_Violation + (*graphInPtr)[i1].Cap_Violation > 
                (*graphInPtr)[i2].Slew_Violation + (*graphInPtr)[i2].Slack_Violation + (*graphInPtr)[i2].Cap_Violation);
}

double violationCheck(CircuitGraph& graphIn, bool write_to_file, ofstream& out, ofstream& vio)
{
        int count_cap_vio = 0;
        int count_slew_vio = 0;
        int count_slack_vio = 0;
        LibParserCellInfo *LPCI;
        LibParserCellInfo *LPCI_;
        double T_Output_Cap;
        double Cap_Violation = 0.0;
        double Slack_Violation = 0.0;
        double Slew_Violation = 0.0;
        lpPtr->default_max_transition = 300.0;// same for all
        
        vector<LibParserPinInfo>::iterator it;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
	boost::graph_traits<CircuitGraph>::out_edge_iterator oi, oi_end ;

	vio << "noVio" << "\t" << "capVio" << "\t" << "slackVio" << "\t" << "capslackVio" << "\t"
        << "slewVio" << "\t" << "capslewVio" << "\t" << "slewslackVio" << "\t" << "allVio" << endl;	
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
	{
                
                vertex_descriptor in = (*vi) ;
                graphIn[ in ].Slew_Violation = 0.0;
                graphIn[ in ].Slack_Violation = 0.0;
                graphIn[ in ].Cap_Violation = 0.0;

                if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)  
                        continue;
                
                LPCI = &(lpPtr->mapStandardLibraryCells[ graphIn[ in ].cellName ]);
                
                //check the slew violation, for input pins of
                //Combinational and Sequential cells
                
                for(it = LPCI->pins.begin(); it != LPCI->pins.end(); it++)
                        if(it->name != "o")//not the output pins
                        {
                                double temp_violation = 0.0;
                                double temp_slack_violation = 0.0;
                                string tmp_name = graphIn[ in ].instanceName + "/" + it->name;
                                
                                if(tpPtr->PinsTiming[tmp_name].riseTransition > lpPtr->default_max_transition)
                                        temp_violation += tpPtr->PinsTiming[tmp_name].riseTransition - lpPtr->default_max_transition;
                                
                                if(tpPtr->PinsTiming[tmp_name].fallTransition > lpPtr->default_max_transition)
                                        temp_violation += tpPtr->PinsTiming[tmp_name].fallTransition - lpPtr->default_max_transition;
                                
                                if(tpPtr->PinsTiming[tmp_name].riseSlack < 0)
                                        temp_slack_violation += abs(tpPtr->PinsTiming[tmp_name].riseSlack);
                                
                                if(tpPtr->PinsTiming[tmp_name].fallSlack < 0)
                                        temp_slack_violation += abs(tpPtr->PinsTiming[tmp_name].fallSlack);
                                
                                graphIn[ in ].Slew_Violation += temp_violation;                                
                                graphIn[ in ].Slack_Violation += temp_slack_violation;                                
                                Slew_Violation += temp_violation;
                        }
                
                //check the slack violation, for sequential gates
                if(graphIn[ in ].gateType == 3)
                {
                        for(it = LPCI->pins.begin(); it != LPCI->pins.end(); it++)
                                if(it->name != "o" && it->name != "ck")//not the output pins
                                {
                                        double temp_violation = 0.0;
                                        string tmp_name = graphIn[ in ].instanceName + "/" + it->name;
                                        
                                        if(tpPtr->PinsTiming[tmp_name].riseSlack < 0)
                                                temp_violation += abs(tpPtr->PinsTiming[tmp_name].riseSlack);
                                        
                                        if(tpPtr->PinsTiming[tmp_name].fallSlack < 0)
                                                temp_violation += abs(tpPtr->PinsTiming[tmp_name].fallSlack);
                                        
                                        //for sequential circuit, should be
                                        //only one net connect to DFF input
                                        
                                        graphIn[ in ].Slack_Violation += temp_violation;
                                        Slack_Violation += temp_violation;
                                }
                }
                
                //check the output cap violation
                T_Output_Cap = 0.0;
                for ( boost::tie(oi, oi_end) = boost::out_edges(in, graphIn) ; oi != oi_end ; ++oi )
                {
                        //finding the pin connect to PI, for driver,
                        //instance name and pin name and cell name are same
                        
                        //check the slack and slew violation, for POs
                        if(graphIn[target((*oi), graphIn)].gateType == 1)//is PO
                        {
                                double temp_slack_violation = 0.0;
                                double temp_slew_violation = 0.0;
                                        
                                if(tpPtr->PortsTiming[graphIn[(*oi)].netName].riseSlack < 0)
                                        temp_slack_violation += abs(tpPtr->PortsTiming[graphIn[(*oi)].netName].riseSlack);
                                
                                if(tpPtr->PortsTiming[graphIn[(*oi)].netName].fallSlack < 0)
                                        temp_slack_violation += abs(tpPtr->PortsTiming[graphIn[(*oi)].netName].fallSlack);
                                
                                if(tpPtr->PinsTiming[graphIn[(*oi)].netName].riseTransition > lpPtr->default_max_transition)
                                        temp_slew_violation += tpPtr->PinsTiming[graphIn[(*oi)].netName].riseTransition - lpPtr->default_max_transition;
                                
                                if(tpPtr->PinsTiming[graphIn[(*oi)].netName].fallTransition > lpPtr->default_max_transition)
                                        temp_slew_violation += tpPtr->PinsTiming[graphIn[(*oi)].netName].fallTransition - lpPtr->default_max_transition;
                                
                                Slack_Violation += temp_slack_violation;
                                Slew_Violation += temp_slew_violation;

                                //for PO, the violation comes from its
                                //fan-in gates
                                graphIn[source((*oi), graphIn)].Slack_Violation += temp_slack_violation;                                
                                graphIn[source((*oi), graphIn)].Slew_Violation += temp_slew_violation;                                
                        }
                                                
                        //net name should be unique
                        T_Output_Cap += graphIn[(*oi)].netParasiticLoad;
                        
                        LPCI_ = &(lpPtr->mapStandardLibraryCells[graphIn[target((*oi), graphIn)].cellName]); 
                        //cout<<"Target Pin name is "<<graphIn[(*oi)].targetPinName<<endl;
                        
                        for( it = LPCI_->pins.begin(); it != LPCI_->pins.end(); it++ )
                                if(it->name == graphIn[(*oi)].targetPinName)
                                        break;
                        
                        if(it != LPCI_->pins.end())
                                T_Output_Cap += (*it).capacitance;
                        else//should go back to the spec file for the output load
                                T_Output_Cap += sdcPtr->outputs_load[graphIn[(*oi)].netName];
                }
                
                for( it = LPCI->pins.begin(); it != LPCI->pins.end(); it++ )
                        if(it->name == "o")
                                break;
                
                if(it == LPCI->pins.end())
                        cout<<"No Way"<<endl;
                
                //cout<<"node cell name is "<< graphIn[in].cellName << endl;
                //cout<<"node instance name is "<< graphIn[in].instanceName << endl;
                //cout<<"Max cap is "<< (*it).maxCapacitance << endl;
                if( (*it).maxCapacitance < T_Output_Cap){
                        Cap_Violation += T_Output_Cap - (*it).maxCapacitance;
                        graphIn[ in ].Cap_Violation += T_Output_Cap - (*it).maxCapacitance;
                }
        }
        
        int stat[8];
        memset(stat, 0, 8 * sizeof(int));
        int flag1, flag2, flag3;
        
        int vn = boost::num_vertices(graphIn) ;
        for(int i = 0; i < vn; i++)
        {
                flag1 = 0;
                flag2 = 0;
                flag3 = 0;
                
                if(graphIn[ i ].Cap_Violation > 0.0)
                {
                        flag1 = 1;
                        count_cap_vio++;
                }
                if(graphIn[ i ].Slack_Violation > 0.0)
                {
                        flag2 = 1;
                        count_slack_vio++;
                }
                if(graphIn[ i ].Slew_Violation > 0.0)
                {
                        flag3 = 1;
                        count_slew_vio++;
                }
                
                stat[flag1 + flag2 * 2 + flag3 * 4]++;
        }
        
        for(int i = 0; i < 8; i++)
        {
                switch(i){
	                case 0: //vio<<"Number of gates with no violations "<<stat[0]<<endl;
				vio<<stat[0]<<"\t";
                                break;
                        case 1: //vio<<"Number of gates with only cap violation "<<stat[1]<<endl;
				vio<<stat[1]<<"\t";
                                break;
                        case 2: //vio<<"Number of gates with only slack violation "<<stat[2]<<endl;
				vio<<stat[2]<<"\t";
                                break;
                        case 3: //vio<<"Number of gates with both cap and slack violations "<<stat[3]<<endl;
				vio<<stat[3]<<"\t";
                                break;
                        case 4: //vio<<"Number of gates with only slew violation "<<stat[4]<<endl;
				vio<<stat[4]<<"\t";
                                break;
                        case 5: //vio<<"Number of gates with both cap and slew violations "<<stat[5]<<endl;
				vio<<stat[5]<<"\t";
                                break;
                        case 6: //vio<<"Number of gates with both slack and slew violations "<<stat[6]<<endl;
				vio<<stat[6]<<"\t";
                                break;
                        case 7: //vio<<"Number of gates with all types of  violations "<<stat[7]<<endl;
				vio<<stat[7]<<endl;
                                break;
                        default:
                                break;
                }
        }
        
        //for testing
        /*
        for(int i = 0; i < vn; i++)
        {
                cout<<"Cell Name: "<< graphIn[ i ].cellName <<endl;
                cout<< "Slack Violation is  "<< graphIn[ i ].Slack_Violation << "\t"
                    << "Slew Violation is  "<< graphIn[ i ].Slew_Violation << "\t"
                    << "Cap Violation is  "<< graphIn[ i ].Cap_Violation << endl;
        }
        */
        
        double Total_Violation = Slew_Violation + Slack_Violation + Cap_Violation;
        if(write_to_file)
        {
                out << Slack_Violation << "\t" << Slew_Violation 
                    << "\t" << Cap_Violation << "\t" << Total_Violation 
                    << "\t" << graphIn.accumTotalLeakage(lpPtr->mapStandardLibraryCells) 
                    << "\t" << count_slack_vio << "\t" << count_slew_vio << "\t" << count_cap_vio;
        }
        else
        {
                cout<<"From my Evaluation script....."<<endl;
                cout<< "Slack Violation is " << Slack_Violation << endl;
                cout<< "Slew Violation is " << Slew_Violation <<endl;
                cout<< "Cap Violation is " << Cap_Violation << endl;
                cout<< "Total Violation is " << Total_Violation << endl;
                cout << "The leakage power is : " << graphIn.accumTotalLeakage(lpPtr->mapStandardLibraryCells) << endl ;
        }
        
        out.close();
        return Total_Violation;
}

int main (int argc, char* argv[]) 
{
        
	string contest_dir, benchmark_dir ;
	
	contest_dir = getenv ( "ISPD_CONTEST_ROOT" ) ; 
	benchmark_dir = getenv ( "ISPD_CONTEST_BENCHMARK" ) ;
        cout<<"Benchmark directory is "<<benchmark_dir<<endl;
        
	clock_t startReadStandardLibraryCell = clock() ;
	// 1. Read Standard Library Cell
	//------------------------------------------------------------------------------------
	string standardLibFile = contest_dir+"/lib/contest.lib" ;
	cout << "Start reading standard library cell ( " <<  standardLibFile << " )" << endl ;
	ExtendedLibParser lp (standardLibFile) ;
	lp.readStandardLibraryCell () ;
        lpPtr = &lp;
        //cout<<"Default Max Transition is " << lpPtr->default_max_transition <<endl;

        // 2. Read Sdc File
        string sdcFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".sdc" ;
        ExtendedSdcParser sdcp(sdcFile);
        sdcp.readSdcFile();
        sdcPtr = &sdcp;
	//------------------------------------------------------------------------------------
	clock_t endReadStandardLibraryCell = clock() ;
	
	
	clock_t startReadSpef = clock() ;
	// 3. Read Parasitic Load
	//-----------------------------------------------------------------------------------
	string spefFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".spef" ;
	cout << "Start reading parasitic load from " << spefFile << endl ;
	ExtendedSpefParser sp (spefFile) ;
	sp.readSpefFile () ;
	//-----------------------------------------------------------------------------------
	clock_t endReadSpef = clock() ;
	
	// 4. Initialize the circuit graph
	//-----------------------------------------------------------------------------------
	cout << "The graph is initialized..." << endl;
	CircuitGraph inputCircuit ;
        graphInPtr = &inputCircuit;
	//-----------------------------------------------------------------------------------
        
	
	clock_t startReadVerilog = clock() ;
	// 5. Read Verilog File
	//-----------------------------------------------------------------------------------
	string verilogFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".v" ;
	cout << "Start reading verilog file " << verilogFile << endl ;
	ExtendedVerilogParser vp (verilogFile) ;
	vp.readVerilogFile(inputCircuit, lp.mapStandardLibraryCells) ;
	cout << "End reading verilog file..." << endl ;
	//-----------------------------------------------------------------------------------
	clock_t endReadVerilog = clock() ;
	
	// 6. Udate edges in graph
	//-----------------------------------------------------------------------------------
	inputCircuit.updateConnectivity(vp.verilogConnectivity, sp.mapNetParasiticLoads) ;
	//-----------------------------------------------------------------------------------
	
	//inputCircuit.printGateProperty < string, gateProperty> (&gateProperty::cellName) ;
	
	cout << "The graph is ready for optimization..." << endl ;
        cout.precision(3) ;
	double leakageBefore = inputCircuit.accumTotalLeakage(lp.mapStandardLibraryCells) ;
	//double leakageBefore = 0.0;
	clock_t startOptimization = clock() ;
        
        /*Amir's part......................*/
        //6. Read .sdc file                                                                                                      
        // -----------------------------------------------------------------------------------
        //string sdcFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".sdc" ;
        cout << "Start reading sdc file " << sdcFile << endl ;
        ExtendedSdcParser sdp (sdcFile) ; 
        string clockName ;
        string clockPort ;
        double period ;
        
        sdp.read_clock (clockName, clockPort, period) ;
        sdp.readInputDelays () ;
        sdp.readInputDrivers () ;
        sdp.readOutputDelays () ;
        sdp.readOutputLoads () ;
        sdp.updateOutputCap(inputCircuit) ;
        sdp.updateInputTime (inputCircuit, lp) ;
        //----------------------------------------------------------------------------------
        //cout << "The graph is ready for optimization..." << endl ;
        // Find the range of Output Capacitance for each gate and store
        // that in a file                                            
        //----------------------------------------------------------------------------------------------                          
        string capFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + "_capRange.txt" ;
        ofstream outCapacitance(capFile.c_str()) ;
        StoreOutCapacitance storeFunc ; 
        inputCircuit.updateMinMaxOutputCap (lp, storeFunc, outCapacitance, true) ;
        //----------------------------------------------------------------------------------------------                          

        // fill each node in the circuit with library cells that have no
        // output violations                                        
        inputCircuit.fillNoCapViolationCells (lp) ;
        /*Amir's part......................*/
        
	// 7. Initialize optimization, 
        // (1) size all cells to maximum power saving
	
	Optimization op (contest_dir, benchmark_dir, 3, &minLeakage) ;
	
	cout << "Call updater function..." << endl ;
	op.updateCellType (inputCircuit) ;
        
	cout << "Make the sizer file... " << endl ;
	op.buildSizerFile (inputCircuit, false) ;
        
        //(2) delete the timing file first, otherwise will have interface error 
        string timingFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".timing" ;
	//ifstream infile(timingFile.c_str());
        //if(infile)
        //remove( timingFile.c_str() );
        
        cout << "Call timer..." << endl ;
        op.callTimer() ;
        cout << "Finished!!!" << endl ;
        clock_t endOptimization = clock() ;
        
        //(3) parsing the timing file for the first time 
        ExtendedTimingParser tp (timingFile) ;
        tp.readTimingFile();
        tpPtr = &tp;
        //tp.closeTimingFile();
        
        //(4) divided into cones and optimize each        
        Cone myCone;
        myCone.number_dffs(inputCircuit);
        myCone.TpPtr = &tp;
        myCone.LpPtr = &lp;
        myCone.clear_flag(inputCircuit);
        //myCone.topo_order_whole_graph(inputCircuit); 
        myCone.topo_order_dffs(inputCircuit); 
        myCone.clear_flag(inputCircuit);
        myCone.form_cones(inputCircuit);
        myCone.reset_cones();
        myCone.clear_flag(inputCircuit);
        
        string temp_dir = contest_dir + "/" + benchmark_dir;
        cout<<temp_dir<<endl;
        violation_ctrl MyVc(benchmark_dir,standardLibFile);
        
        //may needs many rounds to reach a zero-violation solution        
        int count = 0;

        bool write_to_file = true;
        string resultFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".result2";
        ofstream out(resultFile.c_str());
        cout << " File name is "<< resultFile.c_str() <<endl;

        out<<"Benchmark"<<"\t\t\t\t\t"<<benchmark_dir<<endl;
        out<<"Round"<<"\t"<<"Slack_Vio"<<"\t"<<"Slew_Vio"<<"\t"
           <<"Cap_Vio"<<"\t"<<"Total_Vio"<<"\t"<<"Leakage"<<"\t"
           <<"#gateSalckVio"<<"\t"<<"#gateSlewVio"<<"\t"
           <<"#gateCapVio"<<"\t"<<"Cur Best"<<"\t"<<"Runtime"<<endl;
        
        string violationFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".vio" ;
        ofstream vio(violationFile.c_str());
        int coarse = 0; double pre_itr_vio = 0.0;
        double temp_total_vio,cur_best_total_vio = 1.0e20;
        
        while(MyVc.check_violations(!write_to_file, out) == false){
                out<<count<<"\t";
                temp_total_vio = violationCheck(inputCircuit, write_to_file, out, vio);
                
                myCone.update_DFF_timing(inputCircuit);
                myCone.normal_cone_sorting();
                myCone.form_hybrid_cones(inputCircuit);
                myCone.hybrid_cone_sorting();

                out.open(resultFile.c_str(), ios::app);
                if(cur_best_total_vio > temp_total_vio)
                        cur_best_total_vio = temp_total_vio;
                out<< "\t" << cur_best_total_vio << "\t";

                vio<<"New Round"<<endl;
                //jump-out condition
                /*
                if(pre_itr_vio < temp_total_vio && ((temp_total_vio - pre_itr_vio)/pre_itr_vio < 0.02) )
                {
                        out<<endl;
                        out<< "Phase 1 ends...."<<endl;
                        out.close();
                        vio.close();
                        break;
                }
                else
                        pre_itr_vio = temp_total_vio;
                */
                cout << "Optimization Round " << count << endl;
                clock_t startSampling = clock();
                bool state = myCone.sampling(inputCircuit, coarse);
                clock_t endSampling = clock();
                out<<(double)(endSampling - startSampling) / (double)CLOCKS_PER_SEC<<endl;
                
                cout << "Call updater function..." << endl ;
                //op.updateCellType (inputCircuit) ;
                cout << "Make the sizer file... " << endl ;
                op.buildSizerFile (inputCircuit, false) ;
                cout << "Call timer..." << endl ;
                op.callTimer() ;
                cout << "Finished!!!" << endl ;
                tp.updateTimingFile(timingFile);
                
                if(state == true)
                {
                        out<< "Phase 1 ends...."<<endl;
                        //if(coarse <= 3)
                        //coarse++;
                }
                //break;
                
                count++;
        }
        
        out.close();
        vio.close();
        //finished and reporting
	cout << "Gate after optimization: " << endl;
	//inputCircuit.printGateProperty < string, gateProperty> (&gateProperty::cellName) ;
	
	cout.precision(3) ;
	cout << "The leakage power is (before optimization): " << leakageBefore << endl ;
	cout << "The leakage power is (after optimization): " << inputCircuit.accumTotalLeakage(lp.mapStandardLibraryCells) << endl ;
	
	//printEdgesProperty <string, netProperty> (sample, &netProperty::netName) ;
        
	cout.precision(5) ;
	double timeStandard = (double)(endReadStandardLibraryCell - startReadStandardLibraryCell) / (double)CLOCKS_PER_SEC ;
	double timeSpef = (double)(endReadSpef - startReadSpef) / (double)CLOCKS_PER_SEC ;
	double timeVerilog = (double)(endReadVerilog - startReadVerilog) / (double)CLOCKS_PER_SEC ; 
	double timeOptimization = (double)(endOptimization - startOptimization) / (double)CLOCKS_PER_SEC ;
	
	cout << "--------------Run time---------------" << endl ;
	cout << "Reading Standard Library Cell: " <<  timeStandard << endl;
	cout << "Reading SPEF: " <<  timeSpef << endl;
	cout << "Reading Verilog: " << timeVerilog << endl;
	cout << "Optimization: " << timeOptimization << endl;
	cout << "Total: " << timeStandard + timeSpef + timeVerilog + timeOptimization << endl ;
	cout << "-------------------------------------" << endl ;
        
           
        return 0;
}
